package chances.framework.jpa.rest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import chances.framework.jpa.RepositoryHelper;

@RestController
public class EntityController {
	@Autowired
	EntityManager entityManager;

	@Autowired
	RepositoryHelper repositoryHelper;

	@SuppressWarnings("rawtypes")
	@RequestMapping("/identity/{entity}/{attr}")
	public ExistResponse exist(@PathVariable("entity") String entity
	        ,@PathVariable("attr") String attr
	        ,@RequestParam("value") String value) {
		List result = entityManager.createQuery("from " + entity + " where " + attr + "=:" + attr)
				.setParameter(attr, value).getResultList();
		if (result.size() > 0) {
			return new ExistResponse(result.get(0));
		} else {
			return new ExistResponse(null);
		}
	}

	@RequestMapping("/genkey/{entity}/{attr}")
	public GenKeyResponse genkey(@PathVariable("entity") String entity, @PathVariable("attr") String attr,
			@RequestParam(name = "perfix", required = false) String preifx,
			@RequestParam(name = "subfix", required = false) String subfix) {
		return new GenKeyResponse(attr + "_" + System.currentTimeMillis());
	}

	@SuppressWarnings("unchecked")
	@RequestMapping("/group/{entity}/{attr}/{key}")
	public List<Object[]> group(@PathVariable("entity") String entity, @PathVariable("attr") String attr,
			@PathVariable("key") String key) {
		List<Object[]> result = entityManager
				.createQuery("select " + attr + ", count(" + key + ") as pcount from Product group by " + attr)
				.getResultList();
		return result;
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@RequestMapping("/enum/{entity}/{name}")
	public List<Object> lookup(@PathVariable("entity") String entity, @PathVariable("name") String name) {
		String key = RepositoryHelper.captureName(entity);
		Class<?> clazz = this.repositoryHelper.getEntityType(key);
		NamedQueries namedqueries = clazz.getAnnotation(NamedQueries.class);
		NamedQuery[] arrays = clazz.getAnnotationsByType(NamedQuery.class);
		Collection<NamedQuery> querys = new ArrayList<NamedQuery>();
		if (Objects.nonNull(namedqueries)) {
			querys.addAll(Arrays.asList(namedqueries.value()));
		}
		if (Objects.nonNull(arrays)) {
			querys.addAll(Arrays.asList(arrays));
		}
		Optional<NamedQuery> result = querys.stream().filter(s -> {
			return name.equalsIgnoreCase(s.name());
		}).findFirst();
		List<Object[]> resultSet = null;
		if (result.isPresent()) {
			Query query = entityManager.createNamedQuery(result.get().name());
			resultSet = query.getResultList();

			return resultSet.stream().map(s -> {
				Map map = new LinkedHashMap<Object, Object>();
				map.put("code", s[0]);
				map.put("name", s[1]);
				return map;
			}).collect(Collectors.toList());

		} else {
			return null;
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@RequestMapping("/enum/{entity}/{name}/{value}")
	public List<Object> lookup(@PathVariable("entity") String entity, @PathVariable("name") String name,
			@PathVariable("value") String value) {
		String key = RepositoryHelper.captureName(entity);
		Class<?> clazz = this.repositoryHelper.getEntityType(key);
		NamedQueries namedqueries = clazz.getAnnotation(NamedQueries.class);
		NamedQuery[] arrays = clazz.getAnnotationsByType(NamedQuery.class);
		Collection<NamedQuery> querys = new ArrayList<NamedQuery>();
		if (Objects.nonNull(namedqueries)) {
			querys.addAll(Arrays.asList(namedqueries.value()));
		}
		if (Objects.nonNull(arrays)) {
			querys.addAll(Arrays.asList(arrays));
		}
		Optional<NamedQuery> result = querys.stream().filter(s -> {
			return name.equalsIgnoreCase(s.name());
		}).findFirst();
		List<Object[]> resultSet = null;
		if (result.isPresent()) {
			Query query = entityManager.createNamedQuery(result.get().name());
            resultSet = query.setParameter(1, value).getResultList();

			return resultSet.stream().map(s -> {
				Map map = new LinkedHashMap<Object, Object>();
				map.put("code", s[0]);
				map.put("name", s[1]);
				return map;
			}).collect(Collectors.toList());

		} else {
			return null;
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
    @RequestMapping("/enum/{entity}/{name}/{value}/{value2}")
    public List<Object> lookup(@PathVariable("entity") String entity, @PathVariable("name") String name,
            @PathVariable("value") String value, @PathVariable("value2") String value2) {
        String key = RepositoryHelper.captureName(entity);
        Class<?> clazz = this.repositoryHelper.getEntityType(key);
        NamedQueries namedqueries = clazz.getAnnotation(NamedQueries.class);
        NamedQuery[] arrays = clazz.getAnnotationsByType(NamedQuery.class);
        Collection<NamedQuery> querys = new ArrayList<NamedQuery>();
        if (Objects.nonNull(namedqueries)) {
            querys.addAll(Arrays.asList(namedqueries.value()));
        }
        if (Objects.nonNull(arrays)) {
            querys.addAll(Arrays.asList(arrays));
        }
        Optional<NamedQuery> result = querys.stream().filter(s -> {
            return name.equalsIgnoreCase(s.name());
        }).findFirst();
        List<Object[]> resultSet = null;
        if (result.isPresent()) {
            Query query = entityManager.createNamedQuery(result.get().name());
            resultSet = query.setParameter(1, value).setParameter(2, value2).getResultList();

            return resultSet.stream().map(s -> {
                Map map = new LinkedHashMap<Object, Object>();
                map.put("code", s[0]);
                map.put("name", s[1]);
                return map;
            }).collect(Collectors.toList());

        } else {
            return null;
        }
    }
}
